package com.evil.enterprise.interseptor;

import cn.hutool.extra.servlet.JakartaServletUtil;
import cn.hutool.http.Header;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.evil.account.api.RemoteLoginService;
import com.evil.common.account.dto.user.UserBaseRespDTO;
import com.evil.common.core.util.ServletUtils;
import com.evil.common.log.annotation.RequestLogAnnotation;
import com.evil.common.log.constant.LogConstant;
import com.evil.common.log.util.LogUtil;
import com.evil.log.api.RemoteOperationLogService;
import com.evil.log.api.RemoteRequestLogService;
import com.evil.log.pojo.dto.RequestLogDTO;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.InputStreamSource;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.WebRequest;

import java.io.InputStream;
import java.net.InetAddress;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;


/**
 * 操作日志日志
 *
 * @author liyang
 * @date 2022-08-08 00:00
 */
@Aspect
@Component
@RequiredArgsConstructor
public class RequestLogInterceptor {

    @Value("${spring.application.name}")
    private String applicationName;

    @Value("${spring.profiles.active}")
    private String environment;

    private final RemoteLoginService loginService;

    private final RemoteRequestLogService requestLogService;

    private final RemoteOperationLogService remoteOperationLogService;

    private final Logger requestLogger = LoggerFactory.getLogger(LogConstant.WEB_REQUEST_LOGGER);

    @Pointcut("execution(public * * (..) ) && (@annotation(com.evil.common.log.annotation.RequestLogAnnotation) || @within(com.evil.common.log.annotation.RequestLogAnnotation))")
    public void logPointCut() {
    }

    @Around("logPointCut() && @annotation(requestLogAnnotation)")
    public Object around(ProceedingJoinPoint joinPoint, RequestLogAnnotation requestLogAnnotation) throws Throwable {
        UserBaseRespDTO loginUser = loginService.getLoginUser();

        RequestLogDTO requestLog = new RequestLogDTO();
        try {
            InetAddress localHost = InetAddress.getLocalHost();
            requestLog.setServerIp(ObjectUtils.isNotEmpty(localHost) ? localHost.getHostAddress() : "");
            requestLog.setServerHost(ObjectUtils.isNotEmpty(localHost) ? localHost.getHostName() : "");
            requestLog.setServiceId(applicationName);
            requestLog.setEnv(environment);
        } catch (Exception addressException) {
            requestLogger.error(addressException.getMessage(), addressException);
        }

        HttpServletRequest request = ServletUtils.getRequest();

        requestLog.setRequestMethod(request.getMethod());

        String requestParams = null;
        String requestParamsFull = null;
        String requestBody = null;
        String requestBodyFull = null;

        try {
            requestParamsFull = JSON.toJSONString(JakartaServletUtil.getParamMap(request));
            requestParams = LogUtil.interceptContent(requestParamsFull);

            Object[] args = joinPoint.getArgs();
            List<?> paramList = new ArrayList<>(Arrays.asList(args))
                    .stream().filter(p -> !(p instanceof ServletResponse
                            || p instanceof ServletRequest || p instanceof WebRequest || p instanceof InputStream
                            || p instanceof InputStreamSource))
                    .collect(Collectors.toList());
            requestBodyFull = JSON.toJSONString(paramList);
            requestBody = LogUtil.interceptContent(requestBodyFull);
        } catch (Exception e) {
            requestLogger.warn("【请求日志】提取请求参数错误: ", e);
        }

        if (requestLogAnnotation.isSaveRequestData()) {
            requestLog.setRequestParams(requestParams);
            requestLog.setRequestBody(requestBody);
        }

        requestLog.setRequestUri(request.getRequestURI());
        requestLog.setRequestUrl(request.getRequestURL().toString());
        requestLog.setClientIp(JakartaServletUtil.getClientIP(request));
        requestLog.setUserAgent(request.getHeader(Header.USER_AGENT.toString()));

        requestLog.setTime(new Date());

        requestLog.setCreatorId(loginUser.getUserId());
        requestLog.setCreatorName(loginUser.getNickName());

        requestLog.setCreated(new Date());

        // 普通日志
        if (requestLogAnnotation.isSaveData()) {
            requestLog = requestLogService.insertRequestLog(requestLog);
            request.setAttribute(LogConstant.HEADER_REQUEST_LOG_ID, requestLog.getRequestLogId());
        }

        // 文件日志需要记录请求内容
        requestLog.setRequestParams(requestParamsFull);
        requestLog.setRequestBody(requestBodyFull);

        LocalDateTime start = LocalDateTime.now();
        requestLogger.info("请求 | ip:{} | 用户:{} | 请求接口:{} | 参数:{}", requestLog.getClientIp(), requestLog.getCreatorId(), requestLog.getRequestUri(), requestLog.getParam());

        // 执行目标方法
        Object result = null;
        try {
            result = joinPoint.proceed();
        } finally {
            // 日志返回结果显示
            String resultStr = "";
            try {
                resultStr = JSON.toJSONString(result);
            } catch (Exception e) {
                requestLogger.warn("请求 | ip:{} | 用户:{} | 请求接口:{} | 返回值JSON化出现异常:", requestLog.getClientIp(), requestLog.getCreatorId(), requestLog.getRequestUri(), e);
            }
            int rstLength = resultStr.length();
            if (StringUtils.isNotBlank(resultStr) && rstLength > LogConstant.MAX_LOG_CHARACTER_LENGTH) {
                resultStr = String.format("请求结果长度:%s，大于%s", resultStr.length(), LogConstant.MAX_LOG_CHARACTER_LENGTH);
            }
            // 获取执行完的时间 打印返回报文
            long handleTime = Duration.between(start, LocalDateTime.now()).toMillis();
            String secondStr = LogUtil.handleTime(handleTime);
            requestLogger.info("返回 | 请求接口:{} | 用户:{}{} | 处理时间:{} 毫秒 | 返回结果:{}", requestLog.getRequestUri(), requestLog.getCreatorId(), secondStr, handleTime, resultStr);

            // 普通日志
            if (requestLogAnnotation.isSaveData()) {
                remoteOperationLogService.insertOperationLog();
            }
        }

        return result;
    }
}
